/**
 * Copyright (C) 2010-2011 J.W.Marsden
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
package cc.plural.jsonij.jpath;

import java.lang.reflect.Array;
import java.util.ArrayList;

import cc.plural.jsonij.JSON;
import cc.plural.jsonij.Value;

/**
 * JPath implementation. Inspired by XPath and <a
 * href="http://goessner.net/articles/JsonPath/">JsonPath</a>.
 * @author J.W.Marsden
 */
public class JPathImp<C extends Component> extends ArrayList<C> {

	/**
	 * Serial UID
	 */
	private static final long serialVersionUID = 3165927117307054584L;

	boolean recordEvaluateTime;

	long lastEvaluateTime;

	public JPathImp() {
		recordEvaluateTime = false;
		lastEvaluateTime = -1;
	}

	public Value evaluate(JSON json) throws JPathRuntimeException {
		Value value = json.getRoot();
		return evaluate(value);
	}

	public Value[] evaluateAll(JSON json) throws JPathRuntimeException {
		Value value = json.getRoot();
		return evaluateAll(value);
	}

	public Value evaluate(Value value) throws JPathRuntimeException {
		Value[] returnValues = evaluateAll(value);
		if (Array.getLength(returnValues) > 0) {
			return returnValues[0];
		} else {
			return null;
		}
	}

	public Value[] evaluateAll(Value value) throws JPathRuntimeException {
		long startTime = 0, finishTime = -1;
		if (recordEvaluateTime) {
			startTime = System.nanoTime();
		}
		ArrayList<Value> currentValues = new ArrayList<Value>();
		ArrayList<Value> nextValues = null;
		currentValues.add(value);

		int count = 0;

		for (int i = 0; i < size(); i++) {
			Component c = get(i);
			nextValues = new ArrayList<Value>();
			if (c.getClass() == SearchComponent.class) {
				throw new JPathRuntimeException("notSupported", SearchComponent.class);
			}
			c.evaluate(currentValues, nextValues);
			currentValues = nextValues;
			count++;
		}

		if (recordEvaluateTime) {
			finishTime = System.nanoTime();
			lastEvaluateTime = finishTime - startTime;
		}

		int size = currentValues.size();
		Value[] returnValues = new Value[size];
		for (int i = 0; i < size; i++) {
			returnValues[i] = currentValues.get(i);
		}
		return returnValues;
	}

	public boolean isRecordEvaluateTime() {
		return recordEvaluateTime;
	}

	public void setRecordEvaluateTime(boolean recordEvaluateTime) {
		this.recordEvaluateTime = recordEvaluateTime;
	}

	public long getLastEvaluateTime() {
		return lastEvaluateTime / 1000L;
	}
}
